import * as React from "react";
import { useContext, useRef, useEffect, useState } from "react";
import { createContext } from "react";
import { MasterObject } from "./master-object";

type MapValueType<T, E> = (value: T) => E;

export function makeProvider<T>(useHook: () => T) {
  const ctx = createContext<MasterObject>({} as MasterObject);

  function Provider({ children }: any) {
    const hookResult = useHook();
    const [hookMaster, setHookMaster] = useState<MasterObject>(
      new MasterObject(hookResult)
    );

    useEffect(() => {
      if (hookMaster) {
        hookMaster.updateValues(hookResult);
      }
    }, [hookResult]);
    return <ctx.Provider value={hookMaster}>{children}</ctx.Provider>;
  }

  function useProvider<E>(mapValues?: MapValueType<T, E>): T & E {
    const content = useContext(ctx);
    const [tick, setTick] = useState(0);
    const [subscription] = useState(content.createSubscription());
    const cache = useRef(undefined as any);
    useEffect(() => {
      subscription.setHandler(() => {
        if (mapValues) {
          const newValues = mapValues(subscription.value);
          const cachedValues: any = cache.current;
          if (!cache.current) {
            cache.current = newValues;
            return;
          }
          for (let key in newValues) {
            if (newValues[key] !== cachedValues[key]) {
              setTick(tick + 1);
              cache.current = newValues;
              break;
            }
          }
        } else setTick(tick + 1);
      });
      return () => subscription.destroy();
    }, [tick]);

    return mapValues
      ? cache.current || mapValues(subscription.value)
      : subscription.value;
  }
  return { Provider, useProvider };
}
